﻿using Assimp;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace testopenTk
{
    public class DrawInfo
    {
        public int Animation = 0;
        public int Vao { get; }
        public int Texture;
        public int Count { get; }
        public int verVbo;
        public int norVbo;
        public int texVbo;
        public int MeshIndex;
        public Material material;
        public Vector3D Location;
        public Vector3D max_box;
        public Vector3D min_box;
        public Vector3D[] vers = null;
        /// <summary>
        /// 绘制一个精灵必须的信息
        /// </summary>
        /// <param name="vao">已经创建的vao，使用时绑定</param>
        /// <param name="texture">已经创建的texture，使用时绑定</param>
        /// <param name="count">绘制的顶点数</param>
        /// <param name="ver">顶点vbo，备用</param>
        /// <param name="nor">法向vbo，备用</param>
        /// <param name="tex">纹理坐标vbo，备用</param>
        /// <param name="index">此文件中的mesh索引，与文件名组合为唯一标识，待改进</param>
        /// <param name="material">此模型的材质信息</param>
        /// <param name="location">此模型的位置信息</param>
        /// <param name="Max">模型的最大点，简单碰撞</param>
        /// <param name="Min">模型的最小点，简单碰撞</param>
        public DrawInfo(int vao, int texture, int count,int ver,int nor,
            int tex,int index, Material material,Vector3D location,
            Vector3D Max,Vector3D Min,Vector3D[] versArray) 
        {
            this.Vao = vao;
            this.Texture = texture;
            this.Count = count;
            this.verVbo = ver;
            this.norVbo = nor;
            this.texVbo = tex;
            this.MeshIndex = index;
            this.material = material;
            this.Location = location;
            this.max_box = Max;
            this.min_box = Min;
            this.vers = versArray;
        }
    }
    public class ReadInfo
    {
        public string FilePath;
        public int MeshIndex;
        public decimal LocationX;
        public decimal LocationY;
        public decimal LocationZ;

        public decimal rotationX;
        public decimal rotationY;
        public decimal rotationZ;

        public ReadInfo(string File,int index,decimal LX,decimal LY,decimal LZ,
            decimal RX,decimal RY,decimal RZ)
        {
            FilePath = File;
            MeshIndex = index;
            LocationX = LX;
            LocationY = LY;
            LocationZ = LZ;
            rotationX = RX;
            rotationY = RY;
            rotationZ = RZ;
        }
    }
    public class AssLoad
    {
        public string filePath = "";
        /// <summary>
        /// 每个drawinfo为文件中每个模型在GPU中的信息
        /// 每个模型的独立信息为 路径+模型索引
        /// </summary>
        public List<DrawInfo> DrawInfoList = new List<DrawInfo>();

        public AssLoad(string FilePath)
        {
            filePath = FilePath;
            AssimpImporter ac = new AssimpImporter();
            Scene sc = ac.ImportFile(FilePath,
                PostProcessSteps.FlipUVs |
                PostProcessSteps.GenerateNormals |
                PostProcessSteps.Triangulate
                );
            //ThreadPool.QueueUserWorkItem(new WaitCallback(TestThr),FilePath);
            //List<Vector3D> vert = new List<Vector3D>();
            //List<Vector3D> nor = new List<Vector3D>();
            //List<Vector3D> tex = new List<Vector3D>();

            

                List<uint> face;
                TextureSlot ts;
                for (int i = 0; i < sc.MeshCount; i++)
                {
                    face = new List<uint>();
                    int _texture = 0;
                    for (int j = 0; j < sc.Meshes[i].FaceCount; j++)
                    {
                        face.Add(sc.Meshes[i].Faces[j].Indices[0]);
                        face.Add(sc.Meshes[i].Faces[j].Indices[1]);
                        face.Add(sc.Meshes[i].Faces[j].Indices[2]);
                        #region 手动排序数组
                        //MyMessage.listbox(sc.Meshes[i].Vertices[sc.Meshes[i].Faces[j].Indices[0]].ToString());
                        //MyMessage.listbox(sc.Meshes[i].Vertices[sc.Meshes[i].Faces[j].Indices[1]].ToString());
                        //MyMessage.listbox(sc.Meshes[i].Vertices[sc.Meshes[i].Faces[j].Indices[2]].ToString());

                        //vert.Add(sc.Meshes[i].Vertices[sc.Meshes[i].Faces[j].Indices[0]]);
                        //vert.Add(sc.Meshes[i].Vertices[sc.Meshes[i].Faces[j].Indices[1]]);
                        //vert.Add(sc.Meshes[i].Vertices[sc.Meshes[i].Faces[j].Indices[2]]);

                        //nor.Add(sc.Meshes[i].Normals[sc.Meshes[i].Faces[j].Indices[0]]);
                        //nor.Add(sc.Meshes[i].Normals[sc.Meshes[i].Faces[j].Indices[1]]);
                        //nor.Add(sc.Meshes[i].Normals[sc.Meshes[i].Faces[j].Indices[2]]);

                        //tex.Add(sc.Meshes[i].GetTextureCoords(0)[sc.Meshes[i].Faces[j].Indices[0]]);
                        //tex.Add(sc.Meshes[i].GetTextureCoords(0)[sc.Meshes[i].Faces[j].Indices[1]]);
                        //tex.Add(sc.Meshes[i].GetTextureCoords(0)[sc.Meshes[i].Faces[j].Indices[2]]);
                        //for (int k = 0; k < sc.Meshes[0].Faces[j].IndexCount; k++)
                        //{
                        //    //MyMessage.listbox(sc.Meshes[i].Vertices[sc.Meshes[i].Faces[j].Indices[k]].ToString());
                        //    //st += sc.Meshes[i].Normals[sc.Meshes[i].Faces[j].Indices[k]].ToString() + " ";

                        //} 
                        #endregion
                    }
                    Vector3D max = sc.Meshes[i].Vertices[0],
                        min = sc.Meshes[i].Vertices[0];
                    for (int a = 1; a < sc.Meshes[i].Vertices.Length; a++)
                    {
                        max.X = sc.Meshes[i].Vertices[a].X > max.X ? sc.Meshes[i].Vertices[a].X : max.X;
                        max.Y = sc.Meshes[i].Vertices[a].Y > max.Y ? sc.Meshes[i].Vertices[a].Y : max.Y;
                        max.Z = sc.Meshes[i].Vertices[a].Z > max.Z ? sc.Meshes[i].Vertices[a].Z : max.Z;
                        min.X = sc.Meshes[i].Vertices[a].X < min.X ? sc.Meshes[i].Vertices[a].X : min.X;
                        min.Y = sc.Meshes[i].Vertices[a].Y < min.Y ? sc.Meshes[i].Vertices[a].Y : min.Y;
                        min.Z = sc.Meshes[i].Vertices[a].Z < min.Z ? sc.Meshes[i].Vertices[a].Z : min.Z;
                    }
                    Vector3D vag = (max + min) / 2;//中点
                                                   //MyMessage.listbox(i.ToString() + ":" + max.ToString() + Environment.NewLine + min.ToString());
                                                   //MyMessage.listbox("vag:" + vag.ToString());
                    for (int a = 0; a < sc.Meshes[i].Vertices.Length; a++)
                    {
                        sc.Meshes[i].Vertices[a] -= vag;
                    }
                    max -= vag;
                    min -= vag;
                    Program.Myform.Invoke(new Action(() =>
                    {
                        //有纹理
                        if (sc.Materials[sc.Meshes[i].MaterialIndex].GetAllTextures().Length > 0)
                        {
                            //获取diffuse纹理，其余暂时不做处理
                            ts = sc.Materials[sc.Meshes[i].MaterialIndex].GetTexture(TextureType.Diffuse, 0);
                            _texture = LoadTexture("Res/" + ts.FilePath);
                        }


                        int vao = GL.GenVertexArray();
                        GL.BindVertexArray(vao);


                        #region default VBO，use now
                        int ibo = GL.GenBuffer();
                        GL.BindBuffer(BufferTarget.ElementArrayBuffer, ibo);
                        GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(sc.Meshes[i].FaceCount * sizeof(uint) * 3),
                            face.ToArray(), BufferUsageHint.StaticDraw);
                        int VertVbo = GL.GenBuffer();
                        if (sc.Meshes[i].HasVertices)
                        {
                            GL.BindBuffer(BufferTarget.ArrayBuffer, VertVbo);
                            GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(sc.Meshes[i].VertexCount * sizeof(float) * 3),
                               sc.Meshes[i].Vertices, BufferUsageHint.StaticDraw);
                            GL.EnableVertexAttribArray(Shader.vPosition);
                            GL.VertexAttribPointer(Shader.vPosition, 3, VertexAttribPointerType.Float, false, 0, 0);

                        }
                        int NorVbo = GL.GenBuffer();
                        if (sc.Meshes[i].HasNormals)
                        {
                            GL.BindBuffer(BufferTarget.ArrayBuffer, NorVbo);
                            GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(sc.Meshes[i].Normals.Count() * sizeof(float) * 3),
                               sc.Meshes[i].Normals, BufferUsageHint.StaticDraw);
                            GL.EnableVertexAttribArray(Shader.vNormal);
                            GL.VertexAttribPointer(Shader.vNormal, 3, VertexAttribPointerType.Float, false, 0, 0);
                        }
                        int TexcVbo = GL.GenBuffer();
                        if (sc.Meshes[i].HasTextureCoords(0))
                        {
                            GL.BindBuffer(BufferTarget.ArrayBuffer, TexcVbo);
                            GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(sc.Meshes[i].GetTextureCoords(0).Length * sizeof(float) * 3),
                                sc.Meshes[i].GetTextureCoords(0), BufferUsageHint.StaticDraw);
                            GL.EnableVertexAttribArray(Shader.vTexCoord);
                            GL.VertexAttribPointer(Shader.vTexCoord, 3, VertexAttribPointerType.Float, false, 0, 0);
                        }
                        #endregion
                        #region mylist VBO
                        //int VertVbo = GL.GenBuffer();
                        //if (sc.Meshes[i].HasVertices)
                        //{
                        //    GL.BindBuffer(BufferTarget.ArrayBuffer, VertVbo);
                        //    GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vert.Count * sizeof(float) * 3),
                        //       vert.ToArray(), BufferUsageHint.StaticDraw);
                        //    GL.EnableVertexAttribArray(Shader.vPosition);
                        //    GL.VertexAttribPointer(Shader.vPosition, 3, VertexAttribPointerType.Float, false, 0, 0);

                        //}

                        //int NorVbo = GL.GenBuffer();
                        //if (sc.Meshes[i].HasNormals)
                        //{
                        //    GL.BindBuffer(BufferTarget.ArrayBuffer, NorVbo);
                        //    GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(nor.Count * sizeof(float) * 3),
                        //       nor.ToArray(), BufferUsageHint.StaticDraw);
                        //    GL.EnableVertexAttribArray(Shader.vNormal);
                        //    GL.VertexAttribPointer(Shader.vNormal, 3, VertexAttribPointerType.Float, false, 0, 0);
                        //}
                        //int TexcVbo = GL.GenBuffer();
                        //if (sc.Meshes[i].HasTextureCoords(0))
                        //{
                        //    GL.BindBuffer(BufferTarget.ArrayBuffer, TexcVbo);
                        //    GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(tex.Count * sizeof(float) * 3),
                        //        tex.ToArray(), BufferUsageHint.StaticDraw);
                        //    GL.EnableVertexAttribArray(Shader.vTexCoord);
                        //    GL.VertexAttribPointer(Shader.vTexCoord, 3, VertexAttribPointerType.Float, false, 0, 0);
                        //} 
                        #endregion

                        DrawInfoList.Add(new DrawInfo(vao, _texture,/*sc.Meshes[i].VertexCount*/face.Count,
                                VertVbo, NorVbo, TexcVbo, i, sc.Materials[sc.Meshes[i].MaterialIndex],
                                vag, max, min, sc.Meshes[i].Vertices));
                        GL.BindVertexArray(0);
                    }));

                }

        
        }
        public void DisposeResoure()
        {
            foreach (DrawInfo t in DrawInfoList)
            {
                if (t.verVbo > 0)
                {
                    GL.DeleteBuffer(t.verVbo);
                }
                if (t.verVbo > 0)
                {
                    GL.DeleteBuffer(t.norVbo);
                }
                if (t.texVbo > 0)
                {
                    GL.DeleteBuffer(t.texVbo);
                }
                if (t.Vao > 0)
                {
                    GL.DeleteVertexArray(t.Vao);
                }
                if (t.Texture > 0)
                {
                    GL.DeleteTexture(t.Texture);
                }

            }
        }
        static int LoadTexture(string TexPath)//加载纹理
        {
            if (String.IsNullOrEmpty(TexPath))
                return 0;
            //MyMessage.show(TexPath);
            //GL.Enable(EnableCap.Texture2D);
            //GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);
            Bitmap bm= new Bitmap(TexPath);
          
           
            int _Texture = GL.GenTexture();
            GL.BindTexture(TextureTarget.Texture2D, _Texture);

            System.Drawing.Imaging.BitmapData bmd = bm.LockBits(new Rectangle(0, 0, bm.Width, bm.Height),
                System.Drawing.Imaging.ImageLockMode.ReadOnly,
                System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            GL.TexImage2D(TextureTarget.Texture2D,
                0,
                PixelInternalFormat.Rgba,
                bm.Width,
                bm.Height,
                0,
                OpenTK.Graphics.OpenGL.PixelFormat.Bgra,
                PixelType.UnsignedByte,
                bmd.Scan0);
            bm.UnlockBits(bmd);
            GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
            //GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMinFilter.Linear);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMinFilter.LinearMipmapLinear);
            //MyMessage.show(ModelInfo._Texture.ToString());
            //MyMessage.lableShow(bm.Height.ToString());
            return _Texture;
        }
    }
}
